﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Vortex.Utils {

	/// <summary>
	/// Provides performance monitor with counters and timers
	/// </summary>
	/// <typeparam name="TCounter">The type of the counter enum.</typeparam>
	/// <typeparam name="TTimer">The type of the timer enum.</typeparam>
	public class PerformanceMonitor<TCounter, TTimer> {
		///<summary>Internal data of monitor timer</summary>
		struct MonitorTimer {
			public PerfTime StartSection;
			public PerfTimeSpan TotalTime;
			public bool Active;

			/// <summary>
			/// Starts this timer.
			/// </summary>
			public void Start() {
				StartSection = PerfTime.Now;
				Active = true;
			}

			/// <summary>
			/// Stops this timer .
			/// </summary>
			public void Stop() {
				TotalTime += PerfTime.Now - StartSection;
				Active = false;
			}

			/// <summary>
			/// Flushes this timer.
			/// </summary>
			public void Flush() {
				if (Active) {
					PerfTime now = PerfTime.Now;
					TotalTime += now - StartSection;
					StartSection = now;
				}

			}
		};

		///<summary>All of times in monitor</summary>
		private MonitorTimer[] _timers;
		///<summary>All of counters in monitor</summary>
		private int[] _counters;

		/// <summary>
		/// Initializes a new instance of the <see cref="PerformanceMonitor&lt;TCounter, TTimer&gt;"/> class. All counter/timers are in default state
		/// </summary>
		public PerformanceMonitor() {
			Reset();
		}

		/// <summary>
		/// Resets this monitor instance to default values.
		/// </summary>
		public void Reset() {
			_timers = new MonitorTimer[Enum.GetValues(typeof(TTimer)).Length];
			_counters = new int[Enum.GetValues(typeof(TCounter)).Length];
		}

		/// <summary>
		/// Copies data of this instance (making snapshot) into another monitor.
		/// </summary>
		/// <param name="target">The target to copy data of this monitor.</param>
		public void CopyTo(PerformanceMonitor<TCounter, TTimer> target) {
			Array.Copy(_timers, target._timers, _timers.Length);
			Array.Copy(_counters, target._counters, _counters.Length);
		}

		/// <summary>
		/// Notifies monitor that code entered some section and timer should be started
		/// </summary>
		/// <param name="timer">The timer enum constant.</param>
		public void StartTimer(TTimer timer) {
			_timers[timer.GetHashCode()].Start();
		}

		/// <summary>
		/// Notifies monitor that code leaved some section and timer should be stopped. Result will be added to previous value of timer
		/// </summary>
		/// <param name="timer">The timer enum constant.</param>
		public void StopTimer(TTimer timer) {
			_timers[timer.GetHashCode()].Stop();
		}

		/// <summary>
		/// Flushes all active timers. So we can read values of all unflushed ("long") timers
		/// </summary>
		public void FlushTimers() {
			for (int n = 0; n < _timers.Length; ++n) {
				_timers[n].Flush();
			}
		}

		/// <summary>
		/// Increments counter with 1
		/// </summary>
		/// <param name="counter">The counter enum constant.</param>
		public void AddCounter(TCounter counter) {
			_counters[counter.GetHashCode()] += 1;
		}

		/// <summary>
		/// Increments counter on specified amount
		/// </summary>
		/// <param name="counter">The counter enum constant.</param>
		/// <param name="amount">The amount of events.</param>
		public void AddCounter(TCounter counter, int amount) {
			_counters[counter.GetHashCode()] += amount;
		}

		/// <summary>
		/// Value of performance timer
		/// </summary>
		public PerfTimeSpan this[TTimer timer] {
			get { return _timers[timer.GetHashCode()].TotalTime; }
		}

		/// <summary>
		/// Value of performance counter
		/// </summary>
		public int this[TCounter counter] {
			get { return _counters[counter.GetHashCode()]; }
		}
	}
}
